home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Night Owl 6
/
Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso
/
029a
/
wc526.zip
/
WC526.ASM
< prev
Wrap
Assembly Source File
|
1991-10-10
|
28KB
|
1,093 lines
TITLE Word Count, WC V5.26. Program to count lines, words and chars.
IFDEF ZTIMER
extrn ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
ENDIF
LOCALS ; Use TASM local labels
dosint MACRO function ;; Call the DOS interrupt
mov ah,function ;; Put function number in AH
int 21h
ENDM
code segment page public use16 'CODE' ; Put code segment first,
code ends
data segment word public use16 'DATA' ; but declare the data values first
; to avoid forward references!
SyntaxMsg label byte
db "Word Count V5.26 (C) Terje Mathisen 1986, 1990, 1991 (486-optimized)",13,10
db 13,10
db ' Count lines, words and characters in text files.',13,10
db ' Syntax: WC [/option] PathSpec [PathSpec .. ]',13,10,10
db ' Options:',13,10
db ' /l : Count LINEs',13,10
db ' /w : Count WORDs',13,10
db ' /c : Count CHARs',13,10
db ' /r : Use CR, LF or CRLF as line terminator',13,10
db " /z : Use Ctrl-Z as EOF mark.",13,10
db ' /t : Only show the Total counts for multiple files.',13,10
db ' /s<list> : Use the chars in <list> as word separators.',13,10
db ' If neither /w, /l or /c is given, all will be counted.',13,10
db ' TAB, LF, CR and SPACE are always treated as separators.',13,10
db ' If no separator list is given, ",.!#%&=?^;:" will be used.',13,10
db '$'
NoFilesMsg db "No files found"
CrLf db 13,10,"$"
OpenErrMsg db "Open error",0dh,0ah,"$"
CloseErrMsg db "Close error",0dh,0ah,"$"
ReadErrMsg db "Read error",0dh,0ah,"$"
RamErrMsg db 'Not enough RAM',13,10,'$'
total$ db " total in $"
files$ db ' files',13,10,'$'
UseCrLf db 0 ; 0 -> Use only LF to count new lines
MakeJumpTable db 1 ; 1 -> Make Jump table before use
ShowTotals db 0 ; 0 -> Show all files
CpuType db ? ; Save Cpu type (2=286,3=386 etc)
EVEN
c_flags db 0 ; Flags for types of count
or_flag db 0FFh ; Assume to count everything
EofChar db 26 ; Assume Ctrl-Z = EOF
SkipEof db 1 ; 0 to use Ctrl-Z as EOF
; Total count of lines, words and chars
l_cnt_t dw 0,0
w_cnt_t dw 0,0
c_cnt_t dw 0,0
f_cnt dw 0
WhiteLen dw offset WhiteEnd - Offset WhiteSpace
WhiteSpace db 9,10,13,' ' ; Use these ctrl chars as whitespace
EndProg label byte ; The EDATA structure starts here
db ',.!#%&=?^;:' ; Default word separators
WhiteEnd label byte
data ends
stack_seg segment public word use16 'STACK' ; To help the linker!
stack_seg ends
dgroup group code, data, stack_seg ; Everything in one segment for .COM
assume cs:dgroup,ds:dgroup,es:dgroup,ss:dgroup
edata struc ; Un-Initialized vars @ endprog (WORD alignment!)
WhiteTable db 120 dup (?) ; Overlayed on EndProg
spaces db 2 dup (?) ; Two leading spaces for filename
filename db 80 dup (?)
name_start dw 1 dup (?) ; Start of name part of pathname
BufferSize dw 1 dup (?) ; Size of file buffer
BufSeg dw 1 dup (?) ; Segment for buffer
JumpSeg dw 1 dup (?) ; Segment for 64 K lookup table
l_cnt dw 2 dup (?)
w_cnt dw 2 dup (?)
c_cnt dw 2 dup (?)
FileBuffer dw 1 dup (?)
dta db 21 dup (?)
dtaattr db ?
dtatime dd ?
dtasize dd ?
dtaname db 13 dup (?)
Last_Char db ? ; Save the last char in the buffer
FileBufferStart db 1 dup (?) ; Dummy, will be overlayed
edata ends
BufSize equ 0F000h ; Try to get 60 kB file buffer
BufferData struc ; Layout of 2nd lookup table
db 256 dup (?) ; First part of lookup
Xlate_Tab db 256 dup (?) ; char-to-type table (temporary)
db 512 * 15 dup (?) ; Rest of lookup table
BufferData ends
l_bit equ 1
w_bit equ 2
c_bit equ 4
StackSize equ 200h ; Use 512B for stack
code SEGMENT
ORG 100h
main proc far
start:
cld
mov bx, 1000h ; We want 64 K initially
mov ah, 4Ah
int 21h ; DOS SetBlock (Want 4kB)
jc @@RamError
mov cx, BufSize
mov [DGROUP:endprog.BufferSize], cx
mov bx, ((type BufferData) - 1) SHR 4 + 1 ; == 200h
mov ah, 48h
int 21h ; Allocate RAM for 2nd lookup table:
jc @@RamError
mov [DGROUP:endprog.BufSeg], ax
mov bx, 1000h ; We want 64 k for state machine
mov ah, 48h
int 21h ; Allocate RAM for jump table & buffer
jnc @@RamOK
@@RamError:
mov dx, offset DGROUP:RamErrMsg
jmp ErrorExit
@@RamOK:
mov [DGROUP:endprog.JumpSeg], ax
lea ax,[DGROUP:endprog.FileBufferStart]
add ax,15
and ax,NOT 15
mov [DGROUP:endprog.FileBuffer],ax
call GetCpuType
call InitTable2 ; Build 8K increment table
; Move default DTA away from the command line which we are parsing
mov dx, offset DGROUP:endprog.dta
mov ah, 1Ah
int 21h ; SET DTA!
; Start parsing any commands given us
mov si,81h ; Start of parameter area
mov word ptr [DGROUP:endprog.spaces],2020h ; Init to 2 spaces
; Here is where the main parsing loop starts.
SkipWhite:
lodsb
CMP al,13
JNE @@NotCR
; We have got the terminating CR. Check if NO filname has been given:
cmp [DGROUP:f_cnt],0
ja @@NearTotal
; No filename given, so run as a filter for STDIN
mov bx,0 ; stdin handle
call do_file ; Count chars, words etc.
mov dx,offset DGROUP:crlf ; WriteLn
dosint 9
@@NearTotal:
jmp @@TestTotal ; Goto end
@@NotCR:
CMP al,' '
JBE SkipWhite
@@HaveParam:
cmp al,'-' ; Allow '-' & '/' to start options
je @@Option
cmp al,'/'
jne @@NotOption
@@Option:
jmp ParseOption
@@NotOption: ; Not an option, so it must be a file
mov di,offset DGROUP:endprog.filename
mov [DGROUP:endprog.name_start],di
mov dx,di ; Save for Find first
@@MoveName:
stosb ; Save in <filename> buffer
cmp al,'\' ; Look for the end of the path
je @@path_end
cmp al,':'
jne @@next_char
@@path_end:
mov [DGROUP:endprog.name_start],di ; The name part starts here
@@next_char:
lodsb
CMP al,' '
JA @@MoveName
mov byte ptr [di],0 ; Make it ASCIIZ
xor cx,cx ;Only normal files
dosint 4eh ;Find first
mov dx, offset DGROUP:NoFilesMsg
JC NearError
push si
@@FileLoop:
mov si,offset DGROUP:endprog.dtaname ; Copy the filename
mov di,[DGROUP:endprog.name_start] ; in after the path
@@MoveTail:
lodsb ; Add the result from FINDFIRST to the
stosb ; path spec from the parameter
OR al,al
JNE @@MoveTail
mov dx,offset DGROUP:endprog.filename ; Filename
mov ax,3D00h ; Open MODE = read
int 21h
mov dx, offset DGROUP:OpenErrMsg
JC NearError
mov bx,ax ; Handle of file
call do_file
dosint 3eh ; Close file
mov dx, offset DGROUP:CloseErrMsg
JC NearError
cmp [DGROUP:ShowTotals],1
JE @@SkipDisplay
mov di,offset DGROUP:endprog.spaces ; Offset of filename w/ spaces
call printz ; print it!
mov dx, offset DGROUP:crlf ; print CR,LF
dosint 9
@@SkipDisplay:
dosint 4fh ;Find next match
JNC @@FileLoop
pop si ; Restore SI (input parser)
dec si ; Re-Read last char (maybe CR?)
jmp SkipWhite ; Restart parsing loop
@@TestTotal:
CMP [DGROUP:ShowTotals],1 ; Have the user asked (/t) for totals?
JE @@ShowTotal
CMP [DGROUP:f_cnt],1 ; Have we got just one file?
JE @@OneFile ; Yes, so no total display
@@ShowTotal:
mov dx, offset DGROUP:NoFilesMsg
JB NearError ; No files found at all!
mov si, offset DGROUP: l_cnt_t
call PrintNum
mov dx,offset DGROUP:total$
dosint 9
mov ax, [DGROUP:f_cnt]
xor dx, dx
mov di, 1
call prtascii
mov dx, offset DGROUP:files$
dosint 9
@@OneFile:
finish:
mov ax,4C00h ; Set 0 for return code
int 21h
main endp
NearError:
jmp ErrorExit
printzero PROC NEAR ; Procedure to print ASCIIZ lowercase
@@p1:
CMP dl,'A'
JB @@NotUpper
CMP dl,'Z'
JA @@NotUpper
add dl,'a'-'A' ; Make it lowercase
@@NotUpper:
dosint 2
inc di
printz: ; PROC Entry HERE!
mov dl,byte ptr [di]
OR dl,dl
jnz @@p1
ret
printzero ENDP
ReadOption proc near
cmp byte ptr [si],' '
ja ParseOption ; Yes, read another option char
jmp SkipWhite
ParseOption: ; Start here, we have a '/' or '-'
lodsb
cmp al,'a'
jb @@NotLower
cmp al,'z'
ja @@NotLower
sub al,'a'-'A'
@@NotLower:
; Is this C, W or L ?
cmp al, 'C'
mov bx, c_bit
je @@SetOption
cmp al, 'W'
mov bl, w_bit
je @@SetOption
cmp al, 'L'
jne @@TestSep
mov bl, l_bit
@@SetOption:
or [DGROUP:c_flags],bl
mov [DGROUP:or_flag],bh ; Set to 0 (No default)
jmp ReadOption
; It might be a list of word separators:
@@TestSep:
cmp al, 'S' ; Separator list
je @@GetSep
cmp al,'T' ; total display?
jne @@TestEof
mov [DGROUP:ShowTotals],1
jmp ReadOption
@@TestEOF:
cmp al, 'Z' ; Stop on Ctrl-Z in files (EOF mark)?
jnz @@TestCrLf
mov [DGROUP:SkipEof], 0
jmp ReadOption
@@TestCrLf:
cmp al, 'R' ; Allow CR as line terminator
mov dx, offset DGROUP:SyntaxMsg
jnz ErrorExit
mov [DGROUP:UseCrLf],1
mov [DGROUP:MakeJumpTable],1
jmp ReadOption
@@GetSep:
mov di, offset DGROUP:EndProg.WhiteTable
@@G1:
lodsb
stosb
cmp al,' '
ja @@G1
dec di
sub di, offset DGROUP:WhiteSpace
mov [DGROUP:WhiteLen],di
mov [DGROUP:MakeJumpTable],1
dec si ; Reload last char
jmp SkipWhite
ReadOption endp
ErrorExit proc near
push cs
pop ds ; In case DS was destroyed
ASSUME DS:DGROUP
mov ah,9
int 21h
mov ax,4C01h
int 21h
ErrorExit endp
do_file proc near
; bx = handle
; Regs:
push bx
push cx
push dx
push si
push di
push bp
; Init local vars (lines, words and chars)
mov di,offset DGROUP:endprog.l_cnt
mov cx,6
xor ax,ax
rep stosw
cmp [DGROUP:MakeJumpTable], al
jz @@1 ; Table already OK
call InitJumpTable ; Initialize table before start
@@1:
inc [DGROUP:f_cnt] ; One more file!
mov es,[DGROUP:endprog.BufSeg]
mov ds,[DGROUP:endprog.JumpSeg]
ASSUME DS:NOTHING, ES:NOTHING
mov [DGROUP:endprog.Last_Char], 10 ; Assume a LF in front of file
@@ReadBuffer:
mov cx, [DGROUP:endprog.BufferSize]
mov dx, [DGROUP:endprog.FileBuffer]
mov bp, dx
push ds
push cs
pop ds
dosint 3fh ;Read a block
pop ds
mov dx, offset ReadErrMsg
JC ErrorExit
mov cx,ax ; Buffer count
JCXZ @@EndOfFile
cmp [DGROUP:SkipEof],0
jne @@SkipEof
push cx es
push cs
pop es
mov di,si
mov al,1Ah
repne scasb
pop es cx
jne @@SkipEof
dec di ; Point to the Ctrl-Z
sub di,si ; Calculate length before the EOF
jbe @@EndOfFile
mov cx,di
sub al,al ; Set Zero flag
@@SkipEof:
pushf
call count ; Count this block (SS:BP)
popf
jne @@ReadBuffer ; Try another!
@@EndOfFile:
mov al,[DGROUP:endprog.Last_Char] ; check if LF was last char
push cs
pop ds
ASSUME DS:DGROUP
push cs
pop es
ASSUME ES:DGROUP
; If the last line did not end in LF, we must add one to the line-count:
mov si, offset DGROUP:endprog.l_cnt
cmp al,10
je @@Normal
cmp [DGROUP:UseCrLf],0
je @@IncLines
cmp al,13
je @@Normal
@@IncLines:
add word ptr [si],1
adc word ptr [si+2],0
@@Normal:
cmp [DGROUP:ShowTotals],1
JE @@SkipPrint
push si
call PrintNum
pop si
@@SkipPrint:
mov cx, 3
mov di, offset DGROUP:l_cnt_t
@@AddLoop: ; Add counts for this file to total
lodsw
add [di],ax
lodsw
adc [di+2],ax
add di,4 ; Point to next value
loop @@AddLoop
pop bp
pop di
pop si
pop dx
pop cx
pop bx
ret
do_file endp
PrintNum proc near
; SI -> lines, words and chars
push bx
push cx
push dx
push di
mov bx, word ptr [DGROUP:c_flags]
or bl, bh ; OR in default bits
mov cx, 3
mov di, 8 ; Min length for lines (+ 1 space)
@@PrtLoop:
lodsw
mov dx,ax
lodsw
shr bx,1
jnc @@Skip
xchg dx,ax
call prtascii
@@Skip:
inc di ; Increment room for each type
loop @@PrtLoop
pop di
pop dx
pop cx
pop ax
ret
PrintNum endp
prtascii proc c near uses bx cx si di,
; Input
; dx:ax : 32 bit unsigned number (Destroyed)
; di : min length (Not modified)
MAXBUFLEN = 32
local len:WORD, buffer:BYTE:MAXBUFLEN
mov [len],di
mov cx,MAXBUFLEN SHR 1
lea di,[buffer]
mov si,ax
mov ax,' '+ ' ' * 256
rep stosw
mov ax,si
mov si,di ; ptr to end of buffer
push bp
mov bp,dx ;save high word
mov bx,10 ; You have ten fingers
mov cx,3 ; Comma distance
or bp,bp ; upper 16 bits zero?
jz @@next16
@@next32:
xor dx,dx ;zero dx for divide
xchg ax,bp ;get high word
div bx
xchg bp,ax ;return low word
div bx
add dl,'0'
dec di
mov [di],dl ; Save char
@@Test32:
or bp, bp ; 32 bit done (BP = 0 )?
jz @@Test16
loop @@next32
mov dl,','
dec di
mov [di],dl ; Save char
mov cl,3
jmp @@next32
@@next16:
xor dx,dx ;zero dx for divide
div bx
add dl,'0'
dec di
mov [di],dl ; Save char
@@Test16:
or ax, ax ; 32 bit done (BP = 0 )?
jz @@Spaces
loop @@next16
mov dl,','
dec di
mov [di],dl ; Save char
mov cl,3
jmp @@next16
@@Spaces:
dec di ; Point to leading space
mov dx,di ; ptr for dos write
pop bp ; Restore wanted length
mov cx,si
sub cx,dx ; CX = Current length
mov si,[len] ; Min length
sub si,cx
jbe @@LenOK ; Yepp, it's long enough!
sub dx,si ; SI > 0
add cx,si
@@LenOK:
mov ah,40h
mov bx,1 ;STDOUT handle
int 21h ; Print buffer on stack
ret
prtascii endp
GetCpuType proc near
pushf ; Save flags, will get trashed!
xor cx,cx ; Assume 808x
xor ax,ax
push ax
popf ; Try to zero all flags!
pushf
pop ax ; Retrieve them!
and ah,0F0h
cmp ah,0F0h ; Are all 4 top bits set?
jz @@GotCpu ; Yes, so we're done (808x!)
inc cx ; Must be 80286 or later
inc cx
mov ax,0F000h
push ax
popf
pushf
pop ax ; Try to set top 4 bits
and ah,0F0h
jz @@GotCPU ; Must be 286, no bits are set
inc cx ; Bravo! This is at least a 386
mov [word ptr JumpCode],850Fh ; JNZ near
mov [word ptr JumpCode+2],offset LoopTop - (offset JumpCode + 4)
mov [byte ptr JumpCode+4],90h ; NOP
@@GotCPU: ; We have 286 or 386, update name
mov [CpuType],cl
popf ; Restore orig. flags
ret
GetCpuType endp
InitTable2 proc near
push ax bx cx si di ds es
mov es,[DGROUP:endprog.BufSeg]
ASSUME ES:NOTHING
cld
xor di, di
mov bh, 4
@@Loop1:
mov si, offset @@Table2
mov bl, 4
@@Loop2:
mov cx, 16
rep movsw
add di, 512 - 32
dec bl
jnz @@Loop2
dec bh
jnz @@Loop1
mov ds,[DGROUP:endprog.BufSeg]
ASSUME DS:NOTHING
mov ch,30
@@Loop3:
mov cl,30
@@Loop4:
mov bx,cx
mov ax,[bx]
xchg bl,bh
mov [bx+32],ax
sub cl,2
jae @@Loop4
sub ch,2
jae @@Loop3
pop es ds di si cx bx ax
ASSUME DS:DGROUP
ret
@@Table2:
; WW WS WC WL SW SS SC SL CW CS CC CL LW LS LC LL
; First table is when last was xW
dw 0h,0h,100h,100h,001h,0h,100h,100h,101h,100h,200h,100h,101h,100h,200h,200h
;xS
dw 1h,1h,101h,101h,001h,0h,100h,100h,101h,100h,200h,100h,101h,100h,200h,200h
;xC
dw 1h,1h,101h,101h,001h,0h,100h,100h,101h,100h,200h,100h,001h,000h,100h,100h
;xL = xL
dw 1h,1h,101h,101h,001h,0h,100h,100h,101h,100h,200h,100h,101h,100h,200h,200h
InitTable2 endp
InitJumpTable proc near
push bx cx dx si di bp ds es
mov es, [DGROUP:endprog.BufSeg]
ASSUME ES:NOTHING
; First: Fill the table with WORD code == 0
mov di, 256
mov cx, 128
xor ax, ax
rep stosw
mov si, offset DGROUP:WhiteSpace
mov cx, [DGROUP: WhiteLen]
mov al, 2 * 4 ; Separator index
mov bh, 1
@@GetWhite:
mov bl, [DGROUP:si] ; Get one char in DI
inc si ; Point to next
mov [es:bx],al ; Write flag value
loop @@GetWhite
; Set CR code to 4 ?
cmp [DGROUP:UseCrLf],0 ; Treat CRLF special?
jz @@SkipCR
mov byte ptr [es:256+13],4 * 4
@@SkipCR:
; Set LineFeed code to 6:
mov byte ptr [es:256+10],6 * 4
mov es, [DGROUP:endprog.JumpSeg]
mov ds, [DGROUP:endprog.BufSeg]
ASSUME ES:NOTHING, DS:NOTHING
xor di,di ; Start of 64K output segment
xor bx,bx ; High byte counter
cmp [CpuType],3
jae @@MakeTable386
ALIGN 2
UNROLL = 4
@@Make1:
mov si,256 ; offset to 256-byte lookup table
mov dl,[bx+si] ; High byte of pair to look up
shr dl,1
shr dl,1 ; move result to lower 2 bits
mov dh,dl ; duplicate in DH so we can do words
mov cx,256 / (UNROLL * 2) ; 32 * 4 WORDs == 256 bytes
ALIGN 2
@@Make2:
REPT UNROLL
lodsw ; Get next 2 chars from lookup table
or ax,dx ; Insert lower 2 bits in AL and AH
stosw ; Store in output table
ENDM
loop @@Make2
inc bl
jnz @@Make1
jmp @@done
@@MakeTable386:
UNROLL = 4
mov bp, UNROLL * 4
ALIGN 4
@@Make3:
.386
mov si,256 ; offset to 256-byte lookup table
mov dl,[bx+si] ; High byte of pair to look up
shr dl,1
shr dl,1 ; move result to lower 2 bits
mov dh,dl ; duplicate in DH so we can do words
mov ax,dx
shl edx,16
mov dx,ax ; EDX has 4 copies of lower 2 bits
mov cx,256 / (UNROLL * 4) ; 16 * 4 DWORDs == 256 bytes
ALIGN 16
@@Make4:
OFFS = 0
REPT UNROLL
mov eax,[si+OFFS] ;; Get next 4 chars from lookup table
or eax,edx ;; Insert lower 2 bits in all 4 bytes
mov [es:di+OFFS],eax ;; Store in output table
OFFS = OFFS + 4
ENDM
.8086
add si,bp
add di,bp
loop @@Make4
inc bl
jnz @@Make3
@@done:
pop es ds bp di si dx cx bx
ASSUME DS:DGROUP, ES:DGROUP
mov [DGROUP:MakeJumpTable], 0 ; Table is OK!
ret
InitJumpTable endp
;----------------------------------------------------------------------------
;
; This is the counting function. It is optimized for 486 CPU's.
;
;----------------------------------------------------------------------------
count proc near
; ss:bp = ptr to data buffer
; ds = segment of double-byte lookup table
; es = segment of index-to-increments table
; cx = # of char's
ASSUME DS:NOTHING, ES:NOTHING
push bx ; File handle, save!
push dx
push di
add [DGROUP:endprog.c_cnt],cx ; Add # of bytes
adc [DGROUP:endprog.c_cnt+2],0
mov di,cx
mov al,[bp+di-1] ; Last char in buffer
mov bh,al
xchg bh,[DGROUP:endprog.Last_Char] ; Save it and retrieve the last
or cl,cl
jz @@even_page
; Fill out the last 256-byte block with duplicates of the last char,
; unless it's a CR or LF, where I use space instead
add di,bp
cmp al,13
je @@IsCr
cmp al,10
jne @@NotLF
@@IsCr:
mov al,' '
@@NotLF:
mov ah,al ; Duplicate for STOSW
push es
push ss
pop es
mov bl,ch
neg cx
xor ch,ch
shr cx,1
jnc @@even
stosb
@@even:
rep stosw
inc bx
mov ch,bl
pop es
@@even_page: ; CH = # of 256-byte blocks
IFDEF ZTIMER
call ZTimerOn
ENDIF
xchg ch,cl
mov si,cx ; Loop counter
mov bh,[DS:bx] ; Lookup last valid state
mov di,[bp] ; Get first pair of chars
add bp,130 ; Point into the middle of the block
OFFS = -130
xor cx,cx ; # of words, 16 bit
xor dx,dx ; # lines in DH and words in DL
xor ax,ax ; # of lines, 16 bit
ALIGN 16
LoopTop:
REPT 64
OFFS = OFFS + 2
mov bl,[di] ; Lookup new BL value
mov di,[bp+OFFS] ; Get 2 chars
add dx,[es:bx] ; BX -> count for words & lines
OFFS = OFFS + 2
mov bh,[di] ; Lookup state value (second byte)
mov di,[bp+OFFS] ; Get next 2 chars
add dx,[es:bx+32] ; BX+32 -> second table
ENDM
; If the previous 256-byte block was 256 LF's, the line count (in DH) will
; have overflowed. I fix that by using an ADC AH,0 to add in the carry.
adc ah, 0 ; Fix overflow (unlikely!)
add bp, 256 ; Fix address for next block
add al, dh ; # of lines
mov dh, 0
adc ah, 0 ; + carry
add cx, dx ; # of words (max 128!)
xor dx, dx ; Reset for next block
dec si ; More loops?
JumpCode label byte ; This code will be patched to a
jz @@done ; JNZ near LoopTop on a 386+ CPU,
jmp LoopTop ; saving one instruction in the loop
@@done:
IFDEF ZTIMER
call ZTimerOff
call ZTimerReport
ENDIF
add [DGROUP:endprog.w_cnt],cx
adc [DGROUP:endprog.w_cnt+2],dx ; == 0
add [DGROUP:endprog.l_cnt],ax
adc [DGROUP:endprog.l_cnt+2],dx ; == 0
pop di
pop dx
pop bx
ret
count endp
code ends
end main